package com.d2rv.model;

import com.d2rv.offscreen.OffScreen3D;
import processing.core.PVector;

/**
 * Describes the bounding box of an obj model
 *
 * @author Darrieutort A., Dupouy A., Rossignol L., Van Poucke A.
 */
public class BoundingBoxModel {

    private PVector position;
    private PVector cornertopleft;
    private PVector cornerbottomright;
    private PVector size;
    private PVector scale;

    /**
     * Default Constructor
     *
     * @param position The position of the bounding box
     * @param size The size of the bounding box
     * @param scale The scale of the bounding box
     */
    public BoundingBoxModel(PVector position, PVector size, PVector scale) {
        this.position = position;
        this.scale = scale;
        this.size = size;
        this.cornertopleft = new PVector(this.position.x - ((this.size.x / 2) * this.scale.x), this.position.y + ((this.size.y / 2) * this.scale.y), this.position.z + ((this.size.z / 2) * this.scale.z));
        this.cornerbottomright = new PVector(this.position.x + ((this.size.x / 2) * this.scale.x), this.position.y - ((this.size.y / 2) * this.scale.y), this.position.z - ((this.size.z / 2) * this.scale.z));
    }

    /**
     * Decides if the point p is in the bounding box or not
     *
     * @param p the point to test
     * @return true if the point p is in the bounding box
     */
    public boolean inBoundingBox(PVector p) {
        return (p.x >= this.cornertopleft.x && p.x <= this.cornerbottomright.x
                && p.y <= this.cornertopleft.y && p.y >= this.cornerbottomright.y
                && p.z <= this.cornertopleft.z && p.z >= this.cornerbottomright.z);

    }

    /**
     * Decides if the bounding box b intersects the current bounding box
     *
     * @param b the bounding box to test
     * @return true if the bounding box intersects the current bounding box
     */
    public boolean intersect(BoundingBoxModel b) {
        if (this.inBoundingBox(b.cornertopleft())) {
            return true;
        }
        if (this.inBoundingBox(new PVector(b.cornertopleft().x, b.cornertopleft().y, b.cornertopleft().z - (this.size.z * this.scale.z)))) {
            return true;
        }
        if (this.inBoundingBox(new PVector(b.cornertopleft().x + (this.size.x * this.scale.x), b.cornertopleft().y, b.cornertopleft().z - (this.size.z * this.scale.z)))) {
            return true;
        }
        if (this.inBoundingBox(new PVector(b.cornertopleft().x + (this.size.x * this.scale.x), b.cornertopleft().y, b.cornertopleft().z))) {
            return true;
        }
        if (this.inBoundingBox(b.cornerbottomright())) {
            return true;
        }
        if (this.inBoundingBox(new PVector(b.cornerbottomright().x, b.cornerbottomright().y, b.cornerbottomright().z + (this.size.z * this.scale.z)))) {
            return true;
        }
        if (this.inBoundingBox(new PVector(b.cornerbottomright().x - (this.size.x * this.scale.x), b.cornerbottomright().y, b.cornerbottomright().z + (this.size.z * this.scale.z)))) {
            return true;
        }
        if (this.inBoundingBox(new PVector(b.cornerbottomright().x - (this.size.x * this.scale.x), b.cornerbottomright().y, b.cornerbottomright().z))) {
            return true;
        }
        return false;
    }

    /**
     * Updates the current bounding box localisation
     *
     */
    private void update() {
        this.cornertopleft = new PVector(this.position.x - ((this.size.x / 2) * this.scale.x), this.position.y + ((this.size.y / 2) * this.scale.y), this.position.z + ((this.size.z / 2) * this.scale.z));
        this.cornerbottomright = new PVector(this.position.x + ((this.size.x / 2) * this.scale.x), this.position.y - ((this.size.y / 2) * this.scale.y), this.position.z - ((this.size.z / 2) * this.scale.z));
    }

    /**
     * Draws the current bounding box
     * @param parent the current offscreen
     */
    public void draw(OffScreen3D parent) {
        parent.pushMatrix();
        parent.noFill();
        parent.stroke(0, 0, 255);
        //parent.translate(this.position.x, this.position.y, this.position.z);
        parent.box(this.size.x * this.scale.x, this.size.y * this.scale.y, this.size.z * this.scale.z);
        parent.popMatrix();
    }

    /**
     * Sets the position parameter
     * @param position The new position
     */
    public void position(PVector position) {
        this.position = position;
        this.update();
    }

    /**
     * Returns the position parameter
     * @return the position
     */
    public PVector position() {
        return this.position;
    }

    /**
     * Returns the left corner of the bounding box
     * @return the left corner of the bounding box
     */
    public PVector cornertopleft() {
        return this.cornertopleft;
    }

    /**
     * Returns right corner of the bounding box 
     * @return the right corner of the bounding box
     */
    public PVector cornerbottomright() {
        return this.cornerbottomright;
    }

    /**
     * Returns the size of the bounding box
     * @return the size 
     */
    public PVector size() {
        return this.size;
    }

    /**
     * Sets the scale of the boundingBox
     * @param scale The new scale 
     */
    public void scale(PVector scale) {
        this.scale = scale;
        this.size = PVector.mult(this.size, this.scale);
        this.update();
    }

    /**
     * Returns the scale 
     * @return the scale 
     */
    public PVector scale() {
        return this.scale;
    }
}